package com.haruhi.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haruhi.entity.FileDetail;
import com.haruhi.entity.UploadingFile;
import com.haruhi.entity.vo.ResponseEntity;
import com.haruhi.entity.vo.UploadJsonObj;
import com.haruhi.service.FileDetailService;
import com.haruhi.service.UploadingFileService;
import com.haruhi.utils.FileUtil;
import com.haruhi.utils.ParamUtil;
import com.haruhi.utils.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
public class UploadController {

    private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
    @Autowired
    private FileUtil fileUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private FileDetailService fileDetailService;

    @Autowired UploadingFileService uploadingFileService;
    @Value("${fileConfig.filePath}")
    private String filePath;

    @Value("${fileConfig.freeSpacePath}")
    private String freeSpacePath;
    /**
     * 这里需要注意，这个接口是被循环请求的
     * 一些操作发生异常时可能需要前端去停止请求
     * @param response
     * @param detail
     * @param file
     * @param token
     * @return
     */
    @PostMapping("/upload")
    public ResponseEntity upload(HttpServletResponse response, @RequestParam("jsonStr") String detail, @RequestParam("file")MultipartFile file,@RequestParam String token ,@RequestParam String uploader) {
        response.setCharacterEncoding("utf-8");
        if(ParamUtil.isEmpty(detail) || ParamUtil.isEmpty(token) || ParamUtil.isEmpty(uploader)){
            return ResponseEntity.fail(-2,"参数错误");
        }
        if(ObjectUtils.isEmpty(file) || file.getSize() <= 0L){
            return ResponseEntity.fail(-2,"文件为空!");
        }


        File diskPartition = new File(freeSpacePath);
        // 总空间
//        long totalCapacity = diskPartition.getTotalSpace();
        // 可用
        long freePartitionSpace = diskPartition.getFreeSpace();
        LOG.info("服务器可用空间: {} GB",(float) freePartitionSpace / 1024f / 1024f / 1024f);
        // 单位BG
        if((float) freePartitionSpace / 1024f / 1024f / 1024f <= 10f){
            return ResponseEntity.fail(500,"服务器空间不足！");
        }
        if(ParamUtil.isEmpty(token) || -2L == redisUtil.getExpire("token") || !((String)redisUtil.get("token")).equals(token)){
            // 权限异常时前端停止发送请求
            LOG.info("检查即将上传的文件时 token异常: token={}",token);
            return ResponseEntity.fail(401,"权限异常或令牌失效，请联系凉宫春日");
        }
        ObjectMapper objectMapper = new ObjectMapper();

        UploadJsonObj uploadBody = null;

        try {
            uploadBody = objectMapper.readValue(detail, UploadJsonObj.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return ResponseEntity.fail(-2,"参数错误");

        }

        FileDetail fileDetail = fileUtil.partUpload(response, file, uploadBody);
        // 不为空表示已经合并完成了
        if(null != fileDetail && !ParamUtil.isEmpty(fileDetail.getRealName())){

//            fileDetail.setIp(ip);

//            fileDetailService.insert(fileDetail);
            FileDetail condition = new FileDetail();
//            condition.setUploader(uploader);
            condition.setMd5(fileDetail.getMd5());
            condition.setEnabled(false);
            fileDetailService.updateByCondition(fileDetail,condition);
            // 删除用于记录上传中的文件表 实际是更新操作
//            uploadingFileService.deleteByMD5(fileDetail.getMd5());
            UploadingFile condition1 = new UploadingFile();
            condition1.setMd5(fileDetail.getMd5());
            condition1.setIsDel(false);
            UploadingFile param = new UploadingFile();
            param.setIsDel(true);
            param.setName(uploadBody.getName());
            uploadingFileService.updateChunkByMD5(param,condition1);
            return ResponseEntity.success(0,"上传完毕");
        }

        return ResponseEntity.success(-5,"分片文件上传成功");
    }

    /**
     * 验证文件是否存在
     * 以及存在之后的一些操作
     * @param md5
     * @return
     */
    @PostMapping("/check/{md5}/{uploader}")
    public ResponseEntity checkFileMD5(@PathVariable String md5, @PathVariable String uploader, @RequestBody Map<String,String> body){
        if(null == body){
            return ResponseEntity.fail(-2,"参数错误");
        }
        Map resMap = new HashMap<>();

        String name = body.get("name");
        String remark = body.get("remark");
        String ip = body.get("ip");
        Long size = Long.parseLong(body.get("size"));
        // 这个token其实是redis的value
        String token = body.get("token");
        if(ParamUtil.isEmpty(token) || -2L == redisUtil.getExpire("token") || !((String)redisUtil.get("token")).equals(token)){
            // 权限异常时前端停止发送请求
            LOG.info("检查即将上传的文件时 token异常: token={}",token);
            return ResponseEntity.fail(401,"权限异常或令牌失效，请联系凉宫春日");
        }

        if(null == size || size <= 0L || ParamUtil.isEmpty(md5) || ParamUtil.isEmpty(uploader) || ParamUtil.isEmpty(name)){
            LOG.info("检查即将上传的文件时 参数错误: size={}MB,md5={},ip={},uploader={}",size / 1024f / 1024f,md5,ip,uploader);
            return ResponseEntity.fail(-2,"参数错误");
        }

        FileDetail byMD5 = new FileDetail();
        byMD5.setMd5(md5);
        byMD5.setEnabled(true);
        List<FileDetail> byMD5Result = fileDetailService.getByCondition(byMD5);
        // 判断文件是否已经存在
        if(null != byMD5Result && byMD5Result.size() > 0){
            resMap.put("flag",false);
            // 已经存在
            List<String> collect = byMD5Result.stream().map(FileDetail::getUploader).collect(Collectors.toList());
            for (String s : collect) {
                if(uploader.equals(s)){

                    return ResponseEntity.success(-5,"此上传人已经上传过这个文件了",resMap);
                }
            }
            // 已经存在,但是上传人不一样
            FileDetail detail = byMD5Result.get(0);
            detail.setName(name);
            detail.setUploader(uploader);
            detail.setRemark(remark);
            detail.setIp(ip);
            detail.setEnabled(true);
            fileDetailService.insert(detail);

            return ResponseEntity.success(0,"上传成功",resMap);
        }

        // 不存在的文件,需要上传
        resMap.put("flag",true);
        FileDetail condition = new FileDetail();
        condition.setMd5(md5);
        condition.setEnabled(false);
        List<FileDetail> byCondition = fileDetailService.getByCondition(condition);
        // 判断file_detail表是否存在该md5,存在表示之前上传过一些分片,但是未全部上传完
        if(null == byCondition || byCondition.size() == 0){
            // 进入,表示之前完全未上传过,uploading_file表没有该md5的数据
//            FileDetail newDetail = new FileDetail();
            condition.setName(name);
            condition.setUploader(uploader);
            condition.setRemark(remark);
            condition.setIp(ip);
            fileDetailService.insert(condition);

            UploadingFile uploadingFile = new UploadingFile();
            uploadingFile.setName(name);
            uploadingFile.setMd5(md5);
            uploadingFile.setCurrentChunk(0);
            uploadingFile.setIsDel(false);
            uploadingFileService.insert(uploadingFile);
            resMap.put("chunk",0);
            return ResponseEntity.success(0,"OK",resMap);
        }
        // 到这里表示之前上传过一些分片了
        // 获取上传到那一片了
        UploadingFile uploadingFile = new UploadingFile();
        uploadingFile.setMd5(md5);
        uploadingFile.setIsDel(false);
        List<UploadingFile> byCondition1 = uploadingFileService.getByCondition(uploadingFile);
        if(null == byCondition1 || byCondition1.size() == 0 || ParamUtil.isEmpty(byCondition1.get(0).getCurrentChunk())){
            // 表示一片都没有上传,需要全部上传
            resMap.put("chunk",0);
            return ResponseEntity.success(0,"OK",resMap);
        }
        resMap.put("chunk",byCondition1.get(0).getCurrentChunk());
        // 文件不存在服务器,需要上传
        return ResponseEntity.success(0,"OK",resMap);
    }

    @PostMapping("/cancelUpload")
    public ResponseEntity cancelUpload(@RequestBody UploadingFile uploadingFile){
        if(null == uploadingFile || null == uploadingFile.getCurrentChunk() || ParamUtil.isEmpty(uploadingFile.getMd5())){
            return ResponseEntity.fail(-2,"参数错误");
        }
        List<UploadingFile> byCondition = uploadingFileService.getByCondition(uploadingFile);


        if(null != byCondition && byCondition.size() > 0 && !ParamUtil.isEmpty(byCondition.get(0).getName())){
            for (int i = 0; i < uploadingFile.getCurrentChunk(); i++) {
                File file = new File(filePath, i + "_" + byCondition.get(0).getName());
                fileUtil.deleteFile(file);
            }
            uploadingFileService.deleteByMD5(uploadingFile.getMd5(),false);
            fileDetailService.deleteByMD5(uploadingFile.getMd5(),false);
        }

        return ResponseEntity.success(0,"已取消上传");
    }

}
